#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
作者: jena
创建时间: 2025.9

公共工具模块
提供项目通用的工具类和函数
"""

import sys
import os
from typing import TextIO, List, Any
from datetime import datetime
from pathlib import Path


def setup_offline_packages_path(caller_file: str = None) -> bool:
    """
    统一的离线包路径设置函数
    遵循"好品味"原则，消除重复代码
    
    策略：只使用 offline_packages 目录（解压后的包，完全兼容）
    
    Args:
        caller_file: 调用者文件路径，用于计算相对路径
                    如果为None，则使用当前文件路径
    
    Returns:
        bool: 是否成功设置路径
    """
    try:
        # 确定基础路径
        if caller_file is None:
            base_path = Path(__file__).parent.parent
        else:
            base_path = Path(caller_file).parent.parent
        
        # 使用 offline_packages 目录（解压后的包）并同时支持 .whl 离线导入
        offline_packages_dir = base_path / "offline_packages"
        if offline_packages_dir.exists():
            offline_path = str(offline_packages_dir)
            if offline_path not in sys.path:
                sys.path.insert(0, offline_path)

            # 额外：将 offline_packages/_wheels 下的所有 .whl 文件加入 sys.path，支持直接从 wheel 导入
            wheels_dir = offline_packages_dir / "_wheels"
            try:
                if wheels_dir.exists():
                    for wheel in wheels_dir.glob("*.whl"):
                        wheel_path = str(wheel)
                        if wheel_path not in sys.path:
                            sys.path.insert(0, wheel_path)
            except Exception as we:
                import warnings
                warnings.warn(f"添加 wheel 到 sys.path 失败: {we}")

            return True
        else:
            import warnings
            warnings.warn(f"未找到离线包目录: {offline_packages_dir}")
            return False
            
    except Exception as e:
        import warnings
        warnings.warn(f"设置离线包路径时出错: {e}")
        return False


class TeeOutput:
    """
    双向输出类 - 同时输出到多个文件流
    用于将输出同时写入控制台和日志文件
    """
    
    def __init__(self, *files: TextIO):
        """初始化多个输出流"""
        self.files = files
    
    def write(self, text: str) -> None:
        """写入所有输出流"""
        for file in self.files:
            file.write(text)
            file.flush()  # 立即刷新缓冲区
    
    def flush(self) -> None:
        """刷新所有输出流"""
        for file in self.files:
            file.flush()


class LogManager:
    """
    日志管理器 - 统一管理日志输出
    简化日志处理逻辑，提供上下文管理
    """
    
    def __init__(self, log_filename: str, base_dir: str = None):
        """初始化日志管理器
        
        Args:
            log_filename: 日志文件名
            base_dir: 基础目录，默认为当前脚本目录
        """
        if base_dir is None:
            base_dir = os.path.dirname(os.path.abspath(__file__))
        
        # 创建logs目录
        log_dir = os.path.join(base_dir, 'logs')
        os.makedirs(log_dir, exist_ok=True)
        
        self.log_path = os.path.join(log_dir, log_filename)
        self.original_stdout = None
        self.original_stderr = None
        self.log_file = None
        self.tee_output = None
    
    def start_logging(self):
        """开始日志记录"""
        self.log_file = open(self.log_path, 'w', encoding='utf-8')
        self.original_stdout = sys.stdout
        self.original_stderr = sys.stderr
        self.tee_output = TeeOutput(self.original_stdout, self.log_file)
        sys.stdout = self.tee_output
        sys.stderr = self.tee_output
    
    def stop_logging(self):
        """停止日志记录"""
        if self.original_stdout:
            sys.stdout = self.original_stdout
        if self.original_stderr:
            sys.stderr = self.original_stderr
        if self.log_file:
            self.log_file.close()
    
    def __enter__(self):
        """进入上下文管理器"""
        self.log_file = open(self.log_path, 'w', encoding='utf-8')
        
        # 保存原始输出流
        self.original_stdout = sys.stdout
        self.original_stderr = sys.stderr
        
        # 设置双向输出
        sys.stdout = TeeOutput(self.original_stdout, self.log_file)
        sys.stderr = TeeOutput(self.original_stderr, self.log_file)
        
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        """退出上下文管理器"""
        # 恢复原始输出流
        if self.original_stdout:
            sys.stdout = self.original_stdout
        if self.original_stderr:
            sys.stderr = self.original_stderr
        
        # 关闭日志文件
        if self.log_file:
            self.log_file.close()
    
    def get_log_path(self) -> str:
        """获取日志文件路径"""
        return self.log_path


def print_section_header(title: str, width: int = 60) -> None:
    """
    打印章节标题
    
    Args:
        title: 标题文本
        width: 分隔线宽度
    """
    print(f"\n{'=' * width}")
    print(f"{title}")
    print(f"{'=' * width}")


def print_subsection_header(title: str, width: int = 40) -> None:
    """
    打印子章节标题
    
    Args:
        title: 标题文本
        width: 分隔线宽度
    """
    print(f"\n{'-' * width}")
    print(f"{title}")
    print(f"{'-' * width}")


def safe_import(module_name: str, package_name: str = None, 
                fallback_message: str = None) -> tuple:
    """
    安全导入模块，失败时返回None和错误信息
    专为离线环境优化，提供清晰的错误指导
    
    Args:
        module_name: 模块名
        package_name: 包名（用于错误提示）
        fallback_message: 自定义回退消息
    
    Returns:
        (module, error_message): 模块对象和错误信息
    """
    try:
        # 支持多级模块导入，如 'bs4.BeautifulSoup'
        if '.' in module_name:
            parts = module_name.split('.')
            module = __import__(parts[0])
            for part in parts[1:]:
                module = getattr(module, part)
        else:
            module = __import__(module_name)
        return module, None
    except ImportError as e:
        # 为离线环境提供更有用的错误信息
        if fallback_message:
            error_msg = fallback_message
        else:
            error_msg = f"模块 {module_name} 未安装或不可用。"
            if package_name:
                error_msg += f"\n请运行离线安装脚本或手动安装: pip install {package_name}"
            else:
                error_msg += "\n请检查离线依赖包是否正确安装。"
        
        return None, error_msg
    except AttributeError as e:
        error_msg = f"模块 {module_name} 中缺少所需的属性: {e}"
        return None, error_msg


def check_offline_dependencies() -> dict:
    """
    检查离线环境中的依赖包状态
    返回详细的依赖状态报告
    
    Returns:
        dict: 依赖状态字典
    """
    dependencies = {
        'fastapi': {'package': 'fastapi', 'required': True, 'description': 'Web框架(ASGI)'},
        'uvicorn': {'package': 'uvicorn', 'required': True, 'description': 'ASGI服务器'},
        'python_multipart': {'package': 'python-multipart', 'required': False, 'description': '表单上传解析'},
        'requests': {'package': 'requests', 'required': True, 'description': 'HTTP客户端'},
        'bs4': {'package': 'beautifulsoup4', 'required': False, 'description': 'HTML解析'},
        'jieba': {'package': 'jieba', 'required': False, 'description': '中文分词'},
        'opencc': {'package': 'opencc-python-reimplemented', 'required': False, 'description': '繁简转换'}
    }
    
    status_report = {}
    
    for module_name, info in dependencies.items():
        module, error = safe_import(module_name, info['package'])
        status_report[module_name] = {
            'available': module is not None,
            'package': info['package'],
            'required': info['required'],
            'description': info['description'],
            'error': error if module is None else None
        }
    
    return status_report


def get_dependency_summary() -> str:
    """
    获取依赖状态摘要信息
    
    Returns:
        str: 格式化的依赖状态摘要
    """
    status = check_offline_dependencies()
    
    available = [name for name, info in status.items() if info['available']]
    missing_required = [name for name, info in status.items() 
                       if not info['available'] and info['required']]
    missing_optional = [name for name, info in status.items() 
                       if not info['available'] and not info['required']]
    
    summary = f"依赖状态摘要:\n"
    summary += f"  可用: {len(available)}/{len(status)} ({', '.join(available)})\n"
    
    if missing_required:
        summary += f"  缺失(必需): {', '.join(missing_required)}\n"
    
    if missing_optional:
        summary += f"  缺失(可选): {', '.join(missing_optional)}\n"
    
    return summary


def create_log_filename(prefix: str = "log") -> str:
    """
    创建带时间戳的日志文件名
    
    Args:
        prefix: 日志文件名前缀
    
    Returns:
        带时间戳的日志文件名
    """
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    return f"{prefix}_{timestamp}.log"


def get_timestamp() -> str:
    """
    获取当前时间戳字符串
    
    Returns:
        格式化的时间戳
    """
    return datetime.now().strftime("%Y-%m-%d %H:%M:%S")


# 项目统一为 FastAPI 模式


class FastAPIDependencyManager:
    """
    FastAPI依赖管理器 - 统一管理FastAPI相关库导入，仅从离线包路径尝试导入
    """
    def __init__(self):
        self.fastapi = None
        self.Request = None
        self.responses = {}
        self.CORSMiddleware = None
        self.StaticFiles = None
        self.uvicorn = None
        self._init_dependencies()

    def _init_dependencies(self):
        # FastAPI 及其组件
        fastapi_module, fastapi_error = safe_import('fastapi', 'fastapi', 'FastAPI不可用，无法启动ASGI服务')
        if fastapi_module:
            try:
                from fastapi import FastAPI, Request
                from fastapi.responses import JSONResponse, HTMLResponse, FileResponse, PlainTextResponse
                from fastapi.middleware.cors import CORSMiddleware
                from fastapi.staticfiles import StaticFiles
                self.fastapi = FastAPI
                self.Request = Request
                self.responses = {
                    'JSONResponse': JSONResponse,
                    'HTMLResponse': HTMLResponse,
                    'FileResponse': FileResponse,
                    'PlainTextResponse': PlainTextResponse,
                }
                self.CORSMiddleware = CORSMiddleware
                self.StaticFiles = StaticFiles
            except ImportError as e:
                import warnings
                warnings.warn(f"FastAPI组件导入失败: {e}")
        elif fastapi_error:
            import warnings
            warnings.warn(fastapi_error)

        # Uvicorn (用于启动ASGI服务)
        uvicorn_module, _ = safe_import('uvicorn', 'uvicorn', 'Uvicorn不可用，无法以FastAPI方式启动')
        if uvicorn_module:
            try:
                import uvicorn  # type: ignore
                self.uvicorn = uvicorn
            except ImportError:
                self.uvicorn = None

    def has_fastapi(self) -> bool:
        return self.fastapi is not None

    def has_uvicorn(self) -> bool:
        return self.uvicorn is not None

    def get_fastapi(self):
        return self.fastapi

    def get_request_cls(self):
        return self.Request

    def get_responses(self):
        return self.responses

    def get_cors_middleware(self):
        return self.CORSMiddleware

    def get_static_files(self):
        return self.StaticFiles

    def get_uvicorn(self):
        return self.uvicorn